package com.goodwilldd.base.entities;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Entity
@Table(name = "persons")
public class Person {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@ManyToOne
	@JoinColumn(name="familyId", updatable=false, insertable=false)
	Person familyId;

	@Column(name = "creation_time", nullable = false)
	private Date creationTime;

	@Column(name = "first_name", nullable = false)
	private String firstName;

	@Column(name = "last_name", nullable = false)
	private String lastName;

	@Column(name = "modification_time", nullable = false)
	private Date modificationTime;

	@Version
	private long version = 0;

	public Long getId() {
		return id;
	}

	/**
	 * Gets a builder which is used to create Person objects.
	 * 
	 * @param firstName
	 *            The first name of the created user.
	 * @param lastName
	 *            The last name of the created user.
	 * @return A new Builder instance.
	 */
	public static Builder getBuilder(String firstName, String lastName) {
		return new Builder(firstName, lastName);
	}

	public Date getCreationTime() {
		return creationTime;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	/**
	 * Gets the full name of the person.
	 * 
	 * @return The full name of the person.
	 */
	@Transient
	public String getName() {
		StringBuilder name = new StringBuilder();

		name.append(firstName);
		name.append(" ");
		name.append(lastName);

		return name.toString();
	}

	public Date getModificationTime() {
		return modificationTime;
	}

	public long getVersion() {
		return version;
	}

	public void update(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	@PreUpdate
	public void preUpdate() {
		modificationTime = new Date();
	}

	@PrePersist
	public void prePersist() {
		Date now = new Date();
		creationTime = now;
		modificationTime = now;
	}

	@Override
	public String toString() {
		try {
			return (new ObjectMapper()).writerWithDefaultPrettyPrinter()
					.writeValueAsString(this);
		} catch (JsonProcessingException e) {
			return null;
		}
	}

	/**
	 * A Builder class used to create new Person objects.
	 */
	public static class Builder {
		Person built;

		/**
		 * Creates a new Builder instance.
		 * 
		 * @param firstName
		 *            The first name of the created Person object.
		 * @param lastName
		 *            The last name of the created Person object.
		 */
		Builder(String firstName, String lastName) {
			built = new Person();
			built.firstName = firstName;
			built.lastName = lastName;
		}

		/**
		 * Builds the new Person object.
		 * 
		 * @return The created Person object.
		 */
		public Person build() {
			return built;
		}
	}

	/**
	 * This setter method should only be used by unit tests.
	 * 
	 * @param id
	 */
	protected void setId(Long id) {
		this.id = id;
	}
}
